#ifndef __PLAYER_DATA_H__
#define __PLAYER_DATA_H__

#include <vector>
#include <unordered_set>
#include <list>
#include <queue>
#include <memory>
#include <mutex>

#include "Engine/GameGuid.h"
#include "Engine/Point2.hpp"
#include "Engine/Vector3.hpp"

#include "entt/entt.hpp"
#include "nlohmann/json.hpp"

#include "GameEnum.h"
#include "GameType.h"
#include "PlayerDataMachine.h"

using namespace PocketSurvival::Engine;

namespace PocketSurvival
{
    struct ComponentPlayer;
    class PlayerMachine;

    // 地形加载状态，这个是反作弊用到的内容
    enum class LoadTerrainStatue
    {
        NO_LOADING,         // 没加载过
        LOADING,            // 加载中
        LOADING_COMPLETED   // 加载完成
    };

    class PlayerData : public std::enable_shared_from_this<PlayerData>
    {
    public:
        bool readyToRestart;
        bool isDead;
        GameGuid playerGuid;
        ClientPtr client;
        PlayerClass playerClass;
        std::string username;
        PlayerGameMode playerGameMode = PlayerGameMode::Follow;
        GameMode gameMode;

        float progress;
        
        GameGuid groupKey;

        entt::entity entity = entt::null;
        int32_t playerDataID;

        LoadTerrainStatue terrainStatue;

    public:
        int32_t playerIndex;
        // 复活点
        Vector3 spawnPosition;
        // 第一次进入的时间
        int64_t firstSpawnTime;
        // 上一次进入的时间
        int64_t lastSpawnTime;
        // 人物等级
        float level;
        // 皮肤名称
        std::string characterSkinName;
        int32_t spawnsCount;

        ComponentPlayer *componentPlayer = nullptr;

        bool isNewCreate = false;

    private:
        // 请求地形的队列
        std::queue<Point2> m_requestChunkQueue;
        std::mutex m_requestChunkQueueMutex;

        // 状态机
        PlayerMachine *m_machine;
        // 进入服务器的时间
        float m_inServerTime;
        // 完地形请求的次数，注：一次地形请求可能是几十几百个区块
        uint32_t m_loadTerrainCompiletedCount;

    private:
        void handleUsername();

    public:
        PlayerData(ClientPtr theClient);
        ~PlayerData();

        void update(float dt);

        void load(const nlohmann::json &jsonData);
        void save(nlohmann::json &jsonData);
        void loadMsgPackJson(const nlohmann::json &jsonData);
        void saveToMsgPackJson(nlohmann::json &jsonData, bool confuse);


        void updateRequestChunk();
        inline void requestChunk(const Point2 &point);
        inline uint32_t getRequestChunkSize();

        inline void changeMachine(PlayerMachineState state)
        {
            m_machine->changeTo(state);
        }

        inline float getInServerTime();
        inline uint32_t getLoadTerrainCompiletedCount();
    };

    inline void PlayerData::requestChunk(const Point2 &point)
    {
        m_requestChunkQueueMutex.lock();
        m_requestChunkQueue.push(point);
        m_requestChunkQueueMutex.unlock();
    }

    inline uint32_t PlayerData::getRequestChunkSize()
    {
        m_requestChunkQueueMutex.lock();
        uint32_t size = m_requestChunkQueue.size();
        m_requestChunkQueueMutex.unlock();
        return size;
    }

    inline float PlayerData::getInServerTime()
    {
        return m_inServerTime;
    }
    inline uint32_t PlayerData::getLoadTerrainCompiletedCount()
    {
        return m_loadTerrainCompiletedCount;
    }

} // namespace PocketSurvival

#endif
